/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#ifndef __DX_RENDERER_H__
#define __DX_RENDERER_H__

#pragma once

#include <d3dx9.h>
#include <D3dx9math.h>

#include "IRenderer.h"
#include "common/Singleton.h"
#include "ILight.h"
#include "Texture.h"

namespace directx {


/**
 * \ingroup CoreEngine
 *
 * The directx::Renderer class.<br>
 * DirectX implementation for the IRenderer interface
 *
 * \date 08-20-2007
 *
 * \author juckel
 * \author altjohann
 * \author kuehl
 *
 */
class DLL_EXPORT Renderer
:	public IRenderer
{
// Common rendering functions
public:
	Renderer(void);

	bool init( Window* window );
	void resize(int width, int height);

	void clearScene(const math::Vector4 &clearColor = math::Vector4(0,0,0,0));
	void drawCamera(ICamera *cam = NULL);

	void pushMatrix();
	void popMatrix();

	void setupProjection(float fov, float near, float far);

	bool beginScene();
	void endScene();

	bool prepareBuffer();
	void swapBuffer();

	void translate(const math::Vector3 &vec);
	void rotate(float x, float y, float z);
	void scale(float x, float y, float z);
	void applyMatrix(const math::Matrix4 &matrix);

	virtual const math::Matrix4& getViewMatrix() const;

	virtual const math::Matrix4& getProjMatrix() const;

	virtual const math::Matrix4& getWorldMatrix() const;

	virtual math::Matrix4 getViewProjMatrix() const;

	virtual math::Matrix4 getWorldViewProjMatrix() const;

	virtual math::Matrix4 getWorldProjMatrix() const;

	virtual math::Matrix4 getWorldViewMatrix() const;

	void setMousePointer(ITexture* texture, const math::Vector4& color = math::Vector4(1,1,1,1));

	void drawMouse(int x, int y, bool center = false);

	void loadIdentity();

	// Saves the current state block
	void pushState();
	// Restores the last state block
	void popState();

	void setBackBufferSize(const math::Vector2i &size);

	void resetBackBufferSize();

	const math::Vector2i& getBackBufferSize() const;

	bool bindRenderTarget(ITexture *texture, int dest =0);

	bool unbindRenderTarget(int dest=0);

	void drawScreenQuad(int width = -1, int height = -1, int offsetX = 0, int offsetY = 0,float scaleFactor = 1.0f, float texScale = 1.0f);

	// Draws a rectangle on the screen
	void drawRect(int x, int y, int width, int height, const math::Vector4 &color, bool fill = true);
	// Draw a rectangle in world coordinates
	void drawSprite(ITexture* texture, const math::Vector3 &pos, bool transform, const math::Vector4& color = math::Vector4(1,1,1,1));

	void drawLine(const math::Vector3 &from, const math::Vector3 &to, const math::Vector4 &color);

	void drawPlane(const math::Vector3 &normal, const float d, const math::Vector2f &dimension);

	// Enables / Disables wireframe
	void setWireframe(bool value);

//	void drawSphere(float radius);

	void drawSphere(float radius, const math::Vector4 &color = math::Vector4(1,1,1,1));

	void drawBox(float width, float height, float depth, const math::Vector4 &color = math::Vector4(1,1,1,1));

	bool supportsR2VB();
	bool supportsMRT(int numRT = 2);
	bool supportsFP16Filter();

	bool hasMinimumShaderLevel(int majVS, int minVS, int majPS, int minPS);
	void setClipPlane(IDrawPrimitive *primitive);

	virtual const math::Matrix4& createReflectionMatrix(IDrawPrimitive *plane);

	virtual void mouseToWorld(int x, int y, math::Vector3* outStart, math::Vector3* outDir);

	virtual bool supportsHDR();
// DirectX specific Renderer functions
public:
	virtual ~Renderer(void);
	// Creates render resources
	void create();
	// Releases render resources
	void release();

	// Resets the device
	void resetDevice();

	// Reads the available display modes
	bool readAvailableDisplayModes();
	D3DFORMAT getBestDepthStencilFormat(D3DFORMAT backbufferFormat, D3DFORMAT desiredFormat = D3DFMT_D24S8);

	LPDIRECT3DDEVICE9 getD3DDevice() { return pd3dDevice; }
	D3DMULTISAMPLE_TYPE getMultisampleType() const;

	D3DXMATRIX* getMatrix();
	void applyMatrix( D3DXMATRIX matrix );

	bool bDeviceLost;

	bool isGammaSupported();

	void setGammaRamp(float gammaFactor);

	bool IsTextureFormatOk( D3DFORMAT TextureFormat, D3DFORMAT AdapterFormat, DWORD Usage);
	D3DFORMAT getAdapterFormat();

	LPD3DXMESH CreateMappedSphere(float fRad, unsigned int slices, unsigned int stacks);

	void drawPrimitive(IDrawPrimitive *);

    //m_pd3dDevice->SetClipPlane(0, transformedClipPlane);

private:
	D3DCAPS9 d3dCaps;

	D3DPRESENT_PARAMETERS d3dpp;
	D3DMULTISAMPLE_TYPE multisampleType;
	// The Direct3D Device pointer
	LPDIRECT3D9       pD3D;
	// The Direct3D Rendering device
	LPDIRECT3DDEVICE9 pd3dDevice; 
	D3DFORMAT depthBufferFormat;
	// The backbuffer rendering surface
	LPDIRECT3DSURFACE9 pBackBuffer;
	LPDIRECT3DSURFACE9 pDepthStencilSurface;
	// For capturing states
	IDirect3DStateBlock9* pStateBlock;
	// Backbuffer description for width and height querying
	D3DSURFACE_DESC backBufferDesc;
	// The directx matrix stack for push and pop
	LPD3DXMATRIXSTACK dxMatrixStack;
	LPDIRECT3DVERTEXBUFFER9	pLineBuffer;
	LPDIRECT3DVERTEXBUFFER9 pRectBuffer;
	LPDIRECT3DVERTEXBUFFER9 pRectScreenBuffer;
	Texture* pMousePointer;
	LPD3DXSPRITE pSprite;
	
	math::Matrix4 worldMat;
	math::Matrix4 viewMat;
	math::Matrix4 projMat;

	bool bPrevInitialized;
	bool m_bBegin;
	HRESULT hr;
	math::Vector2i m_vBackBuffer;
	math::Vector4 m_vMouseColor;

	math::Matrix4 m_mReflectionMat;
};

} // namespace

#endif
